package com.aims.common.util;

import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.PutObjectResult;
import com.amazonaws.services.s3.model.S3Object;
import com.amazonaws.services.s3.model.S3ObjectInputStream;

import org.apache.commons.lang3.ObjectUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.*;
import java.net.URL;

@Component
public class CephUtil {

    private static Logger logger = LoggerFactory.getLogger(com.aims.common.util.CephUtil.class);

    private static CephProxyClient client;
    private static String bucketName;

    @Value("${ceph.accessKey}")
    private String accessKey;

    @Value("${ceph.secretKey}")
    private String secretKey;

    @Value("${ceph.url}")
    private String endpoint;

    @Value("${ceph.bucketName}")
    private String cephBucketName;

    @PostConstruct
    public void init() throws Exception {
        logger.info("ceph start app start;");
        Configuration config = new Configuration();

        // ak, sk 会在申请接入 CephProxy 之后给业务方
        config.setAk(accessKey);
        config.setSk(secretKey);
        config.setEndpoint(endpoint);
        // Configuration.PROXY_MODE 使用FPS-MARS(CephProxy)代理访问
        config.setMode(Configuration.DIRECT_MODE);
        CephProxyManager.init(config);
        client = CephProxyManager.getCephProxyClient();

        bucketName = cephBucketName;
        logger.info("ceph start app end;");
    }

    /**
     * 关闭Ceph客户端
     */
    public static void close() {
        if (client != null) {
            try {
                client.shutdown();
                logger.info("Ceph client closed successfully");
            } catch (Exception e) {
                logger.error("Failed to close Ceph client", e);
            }
        }
    }

    /**
     * 下载Ceph对象到本地文件
     *
     * @param key  对象的key
     * @param file 本地文件
     */
    public static void getObject(String key, File file) {
        logger.info("Downloading object from Ceph, key: {}, file: {}", key, file.getAbsolutePath());
        try {
            S3Object s3Object = client.getObject(bucketName, key);
            S3ObjectInputStream inputStream = s3Object.getObjectContent();

            // 确保父目录存在
            if (!file.getParentFile().exists()) {
                file.getParentFile().mkdirs();
            }

            FileOutputStream outputStream = new FileOutputStream(file);
            outputInputStream(inputStream, outputStream);

            outputStream.close();
            closeS3Object(s3Object);
            logger.info("Object downloaded successfully");
        } catch (Exception e) {
            logger.error("Failed to download object from Ceph, key: {}", key, e);
            throw new RuntimeException("Download object failed", e);
        }
    }

    /**
     * 下载Ceph对象到本地文件路径
     *
     * @param key      对象的key
     * @param filePath 本地文件路径
     */
    public static void getObject(String key, String filePath) {
        getObject(key, new File(filePath));
    }

    /**
     * 获取Ceph上的对象
     *
     * @param key 对象的key
     * @return S3Object对象
     */
    public static S3Object getObject(String key) {
        logger.info("Getting object from Ceph, key: {}", key);
        try {
            S3Object s3Object = client.getObject(bucketName, key);
            logger.info("Object retrieved successfully");
            return s3Object;
        } catch (Exception e) {
            logger.error("Failed to get object from Ceph, key: {}", key, e);
            throw new RuntimeException("Get object failed", e);
        }
    }

    /**
     * 上传本地文件到Ceph
     *
     * @param key  对象的key
     * @param file 本地文件
     */
    public static void putObject(String key, File file) {
        logger.info("Uploading file to Ceph, key: {}, file: {}", key, file.getAbsolutePath());
        try {
            if (!file.exists()) {
                throw new FileNotFoundException("File not found: " + file.getAbsolutePath());
            }
            client.putObject(bucketName, key, file);
            logger.info("File uploaded successfully");
        } catch (Exception e) {
            logger.error("Failed to upload file to Ceph, key: {}", key, e);
            throw new RuntimeException("Upload file failed", e);
        }
    }

    /**
     * 上传输入流到Ceph（带元数据）
     *
     * @param key      对象的key
     * @param in       输入流
     * @param metadata 对象元数据
     */
    public static void putObject(String key, InputStream in, ObjectMetadata metadata) {
        logger.info("Uploading stream to Ceph with metadata, key: {}", key);
        try {
            client.putObject(bucketName, key, in, metadata);
            logger.info("Stream uploaded successfully");
        } catch (Exception e) {
            logger.error("Failed to upload stream to Ceph, key: {}", key, e);
            throw new RuntimeException("Upload stream failed", e);
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    logger.error("Failed to close input stream", e);
                }
            }
        }
    }

    /**
     * 上传输入流到Ceph
     *
     * @param key 对象的key
     * @param in  输入流
     * @return PutObjectResult
     */
    public static PutObjectResult putObject(String key, InputStream in) {
        logger.info("Uploading stream to Ceph, key: {}", key);
        try {
            ObjectMetadata metadata = new ObjectMetadata();
            PutObjectResult result = client.putObject(bucketName, key, in, metadata);
            logger.info("Stream uploaded successfully");
            return result;
        } catch (Exception e) {
            logger.error("Failed to upload stream to Ceph, key: {}", key, e);
            throw new RuntimeException("Upload stream failed", e);
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    logger.error("Failed to close input stream", e);
                }
            }
        }
    }

    /**
     * 检查Ceph上的对象是否存在
     *
     * @param key 对象的key
     * @return true存在，false不存在
     */
    public static Boolean doesObjectExist(String key) {
        logger.info("Checking if object exists in Ceph, key: {}", key);
        try {
            boolean exists = client.doesObjectExist(bucketName, key);
            logger.info("Object exists: {}", exists);
            return exists;
        } catch (Exception e) {
            logger.error("Failed to check object existence, key: {}", key, e);
            return false;
        }
    }

    /**
     * 将输入流的数据写入输出流
     *
     * @param in  输入流
     * @param out 输出流
     */
    public static void outputInputStream(InputStream in, OutputStream out) {
        try {
            byte[] buffer = new byte[8192];
            int bytesRead;
            while ((bytesRead = in.read(buffer)) != -1) {
                out.write(buffer, 0, bytesRead);
            }
            out.flush();
        } catch (IOException e) {
            logger.error("Failed to transfer data from input stream to output stream", e);
            throw new RuntimeException("Stream transfer failed", e);
        }
    }

    /**
     * 关闭S3Object对象
     *
     * @param s3Object S3Object对象
     */
    public static void closeS3Object(S3Object s3Object) {
        if (s3Object != null) {
            try {
                S3ObjectInputStream inputStream = s3Object.getObjectContent();
                if (inputStream != null) {
                    inputStream.close();
                }
                logger.debug("S3Object closed successfully");
            } catch (IOException e) {
                logger.error("Failed to close S3Object", e);
            }
        }
    }

    /**
     * 生成对象的下载URL
     *
     * @param key 对象的key
     * @return 下载URL
     */
    public static String generateObjectDownloadUrl(String key) {
        logger.info("Generating download URL for object, key: {}", key);
        try {
            // 生成预签名URL，有效期为1小时（3600秒）
            java.util.Date expiration = new java.util.Date();
            long expTimeMillis = expiration.getTime();
            expTimeMillis += 1000 * 60 * 60; // 1小时
            expiration.setTime(expTimeMillis);

            URL url = client.generatePresignedUrl(bucketName, key, expiration);
            String downloadUrl = url.toString();
            logger.info("Download URL generated: {}", downloadUrl);
            return downloadUrl;
        } catch (Exception e) {
            logger.error("Failed to generate download URL, key: {}", key, e);
            throw new RuntimeException("Generate download URL failed", e);
        }
    }

    /**
     * 生成对象的下载URL（自定义过期时间）
     *
     * @param key        对象的key
     * @param expiration 过期时间
     * @return 下载URL
     */
    public static String generateObjectDownloadUrl(String key, java.util.Date expiration) {
        logger.info("Generating download URL for object with custom expiration, key: {}", key);
        try {
            URL url = client.generatePresignedUrl(bucketName, key, expiration);
            String downloadUrl = url.toString();
            logger.info("Download URL generated: {}", downloadUrl);
            return downloadUrl;
        } catch (Exception e) {
            logger.error("Failed to generate download URL, key: {}", key, e);
            throw new RuntimeException("Generate download URL failed", e);
        }
    }

    /**
     * 删除Ceph上的对象
     *
     * @param key 对象的key
     */
    public static void deleteObject(String key) {
        logger.info("Deleting object from Ceph, key: {}", key);
        try {
            client.deleteObject(bucketName, key);
            logger.info("Object deleted successfully");
        } catch (Exception e) {
            logger.error("Failed to delete object from Ceph, key: {}", key, e);
            throw new RuntimeException("Delete object failed", e);
        }
    }

    /**
     * 获取当前使用的bucket名称
     *
     * @return bucket名称
     */
    public static String getBucketName() {
        return bucketName;
    }
}